﻿#include "stdafx.h"
#include "qg/qgasv.h"

//////////////////////////////////////////////////////////////////////////
// 레이아웃
KIMPL_SUBCLASS_ABSTRACT(qgLayout, "AbstractLayout");

// 해시용 클래스
class FakeHashedLayout : public qgLayout
{
	KCH_BASE(FakeHashedLayout, qgLayout);

public:
	static kconstpointer BaseKey(kNodeBase* ptr)
	{
		FakeHashedLayout* self = (FakeHashedLayout*)ptr;
		return &self->_data;
	}

	static bool BaseEq(kNodeBase* ptr, const qgLayoutData& k2)
	{
		FakeHashedLayout* self = (FakeHashedLayout*)ptr;
		const qgLayoutData& k1 = self->_data;

		if (k1.elms.Count() != k2.elms.Count())
			return false;

		for (kint i = 0; i < k1.elms.Count(); i++)
		{
			if (k1.elms[i].type != k2.elms[i].type ||
				k1.elms[i].usage != k2.elms[i].usage ||
				k1.elms[i].index != k2.elms[i].index ||
				k1.elms[i].insts != k2.elms[i].insts)
				return false;
		}

		return true;
	}

	static ksize_t KeyHash(const qgLayoutData& key)
	{
		ksize_t hash = 0;

		for (auto it = key.elms.ConstBegin(); it != key.elms.ConstEnd(); ++it)
			hash = (hash << 5) - hash + ((it->type << 4) | it->usage);

		hash = (hash << 5) - hash + key.elms.Count();

		return hash;
	}
};

//
void qgLayout::TypeInitialize(kType type) throw()
{
	kTsb t(type);
	t.SetFuncKey(&FakeHashedLayout::BaseKey);
	t.SetFuncEq((khEqFunc)&FakeHashedLayout::BaseEq);
	t.SetFuncHash((khHashFunc)&FakeHashedLayout::KeyHash);
}

//
bool qgLayout::GetElement(kint nth, qgLayoutElement* ret)
{
	k_return_value_if_fail(ret, false);
	k_return_value_if_fail((ksize_t)nth < (ksize_t)_data.elms.Count(), false);

	memcpy(ret, &_data.elms[nth], sizeof(qgLayoutElement));

	return true;
}

//
namespace _spp
{
	struct FindLayoutUsage
	{
		qgDloUsage usage;

		FindLayoutUsage(qgDloUsage usage)
			: usage(usage)
		{
		}

		bool operator()(const qgLayoutElement& e)
		{
			return e.usage == usage;
		}
	};
}

//
kint qgLayout::FindUsage(qgDloUsage usage)
{
	auto it = tpl::FindIf(_data.elms.Begin(), _data.elms.End(), _spp::FindLayoutUsage(usage));

	if (it == _data.elms.End())
		return -1;
	else
	{
		// 찾음
		return (kint)(it - _data.elms.Begin());
	}
}

//
qgLayoutData* qgLayout::_VerifyElements(kint count, const qgLayoutElement* elms)
{
	k_return_value_if_fail(count > 0 && elms, NULL);

	qgLayoutData* data = new qgLayoutData();
	data->elms.Resize(count);

	for (kint i = 0; i < count; i++)
	{
		if ((ksize_t)elms[i].type >= QGDLOT_MAX_VALUE ||
			(ksize_t)elms[i].usage > QGDLOU_MAX_VALUE ||	// 이놈만 예외 DX9 때문에
			(ksize_t)elms[i].index >= 16 ||
			(ksize_t)elms[i].slot >= 4)
		{
			kTsb type(qgLayout::Type());
			k_tracep(KLV_ERROR, type.GetName(), KERR_RANGE);

			delete data;
			return NULL;
		}

		data->elms[i] = elms[i];
	}

	return data;
}

